package com.AdvancedDemos.内部类;

public class InnerClass {
    public static void main(String[] args) {
        /**
         * 类中 5 大要素:
         * 成员变量
         * 成员方法
         * 构造函数
         * 代码块(静态,构造,局部)
         *      以及
         * 内部类{
         *  1.成员内部类 [了解]
         *  2.静态内部类 [了解]
         *  3.局部内部类 [了解]
         *  4.匿名内部类 [重点]
         * }
         */


//        Car.Engine aEngine = new Car.Engine();报错:'com.AdvancedDemos.内部类.InnerClass.Car' 不是封闭类
        Car.Engine aEngine = new Car("Tesla Model3").new Engine("涡轮增压");//要先创建外部类对象才能创建成员内部类对象
        aEngine.test();

        Car.Battery aBattery = new Car.Battery();//静态内部类直接通过 外部类名.静态内部类名 的形式来创建对象
        System.out.println(Car.Battery.a);//输出静态内部类中的静态成变量
        aBattery.test();

        Car car = new Car("随便什么名字");
        car.carCapacity.howBig();
        car.carColor.paint();

        System.out.println("-------------------");
//        car.carColor.printHelloworld();//企图通过父类/接口变量调用继承/实现它的匿名内部类中特有的方法,报错无法识别,显然这是不行的,因为父类/接口中并没有该方法,而子类/实现类又恰好是匿名的,无法创建子类/实现类的变量来接匿名内部类的对象,故是无法在类外调用匿名内部类中特有方法的
        /**
         * 通常匿名内部类对象是作为函数实参传递给被调函数的
         */
        paintTheCar(new Color("银色") {
            @Override
            public void paint() {
                System.out.println("将这辆车涂成" + this.getColor());
            }
        });
    }

    /**
     * 用匿名内部类继承的抽象父类变量作为形参,接受调用函数传来的实参匿名内部类对象
     *
     * @param color
     */
    public static void paintTheCar(Color color) {
        System.out.println("--开始上色--");
        color.paint();
        System.out.println("--上色完成--");
    }
}

//汽车类
class Car {
    //引擎类
    public int a = 1;//内外类同名变量
    private String carName;
    static char ch = 'a';//静态成员变量

    static void getChar() {//静态成员函数
        System.out.println("ch = " + ch);
    }

    public Car(String carName) {
        this.carName = carName;
    }

    void test() {//内外类同名函数
        System.out.println("我是外部类中的test");
    }

    public class Engine {//<-成员内部类,直接定义在一个类内部,与普通类无异
        //成员内部类在编译之后会得到 外部类名$内部类名.class
        //成员内部类,在不被外部类使用时,不进行加载
        int a = 2;//内外类同名变量
        private String engineName;
//      public static String schoolName;//从JDK16开始成员内部类才支持静态成员变量

        void test() {//内外类同名函数 ; 内部类就像成员方法一样可以调用外部类的成员变量/方法,包括实例和静态
            System.out.println("--------------------");
            System.out.println("我是内部类中的test");
            System.out.println(carName + "车型的" + engineName + "引擎");//内外类非同名变量直接使用即可

            //使用 外部类名.this 拿到外部类对象
            Car.this.test();//外部类名.this.内外部类同名函数()
            System.out.println("this.a = " + this.a);
            System.out.println("Car.this.a = " + Car.this.a);//外部类名.this.内外类同名变量()
            System.out.println("--------------------");
        }

        public Engine(String engineName) {
            this.engineName = engineName;
        }

        public Engine() {
        }

        public String getEngineName() {
            return engineName;
        }

        public void setEngineName(String engineName) {
            this.engineName = engineName;
        }
    }

    public static class Battery {//<-静态内部类

        //普通成员变量
        private String name;

        //静态成员变量
        public static int a = 100;

        //普通成员方法
        public void test() {

            //在静态内部类中就像在静态成员函数中一样只能直接访问(外部类的)静态成员,要访问外部类中的实例成员,需要创建外部类对象

            //System.out.println(Car.this.a);//报错:无法从 static 上下文引用 'com.AdvancedDemos.内部类.Car.this'
            System.out.println(new Car("在静态内部类中创建的外部类对象").a);

            //只能访问外部类中的静态成员
            System.out.println("ch = " + ch);
            getChar();

            System.out.println("--------------------");
        }


        //构造函数
        public Battery(String name) {
            this.name = name;
        }

        public Battery() {
        }
    }

    void whatever() {
        class 局部内部类 {
            /**
             * 局部内部类是定义在 成员函数(含构造函数),代码块 中的内部类
             *
             * 局部内部类编译之后会得到 外部类名$编号+内部类名.class
             * 生命周期随着方法变化,调用,加载,执行,完毕,销毁
             *
             * 属于是鸡肋语法,爬!
             */
        }
    }

    /**
     * 匿名内部类 (实际上就是一种特殊的局部内部子/实现类) [重点]
     * <p>
     * 匿名指这个类没有名字,只能new一次对象
     * <p>
     * 因为没有类名,所以无法用匿名内部类类型的变量来接new出来的对象,但是可以用父类或者接口类型的变量去接new出来的对象,往往直接return这个对象即可
     * <p>
     * 在编译后的.class字节码文件中,匿名内部类会自动生成一个类通常就是 "外部类名$编号.class",没有自己的名字,它继承/实现了创建匿名内部类对象时指定的父类/接口,并重写方法,再创建此类的对象返回到新建对象处
     *
     * 格式:
     * new 父类或接口(参数值...){
     * 父类或接口中方法的重写;
     * }
     */

    /**
     * 创建了一个继承接口Capacity的实现类的实例,该实现类是类中创建的,又是无名的,所以叫匿名内部类,在创建实例时就同时在实现类中重写了接口中的方法,同时还调用了接口的无参构造函数
     */
    /*
    创建匿名内部类对象完整格式:
    Capacity carCapacity1 = new Capacity() {
        @Override
        public void howBig() {
            System.out.println("这辆车是三厢SUV");
        }
    };
    */
    Capacity carCapacity = () -> System.out.println("这辆车是三厢SUV");//通过lambda表达式简化了匿名内部类实例化


    /**
     * 创建了一个继承抽象类Color的子类的实例,该子类是类中创建的,又是无名的,所以叫匿名内部类,在创建实例时就同时在子类中重写了抽象类中的方法,同时还调用了抽象父类的带参构造函数
     */
    Color carColor = new Color("绿色") {
        @Override
        public void paint() {
            System.out.println("给车涂成" + getColor());
        }

        public void printHelloworld() {
            System.out.println("Hola the World!");
        }
    };

}

abstract class Color {
    private String color;

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public Color(String color) {
        this.color = color;
    }

    public abstract void paint();
}

interface Capacity {
    public abstract void howBig();
}



